
#include "CompanyReport.h"
#include "ZippedObjectMap.h"
#include "BondContainer.h"
#include "qbprotocol/include/simple_encoder.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
//#define new DEBUG_NEW
#endif

//TODO:
#define RTL_FIELD_SIZE(type, field) (sizeof(((type *)0)->field))
#define FIELD_OFFSET(type, field)    offsetof(type, field)
//////////////////////////////////////////////////////////////////////
// class CBondCompanyRep

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

/*
	class ReportVec {
	public:
		typedef std::pair<std::string, REPORT> value_type;
		typedef std::vector<value_type> Vector;
		class iterator{
		public:
			value_type* operator ->();
			bool operator !=(const iterator& it)const;
			iterator& operator++();//前缀
			const iterator operator++(int);//后缀
		};
		class const_iterator{
		public:
			const value_type* operator ->()const;
			bool operator !=(const const_iterator& it)const;
			const_iterator& operator++();
		};

		int size()const;
		void clear(){}
		REPORT& operator[](const std::string& key);
		iterator find(const std::string& key)const;
		iterator begin();
		const_iterator begin()const;
		iterator end();
		const_iterator end()const;
		iterator erase(iterator it);
	};

*/
namespace{

	enum kListedMarketType{
		kListedMarketTypeNone,
		kListedMarketTypeCIB,
		kListedMarketTypeSSE,
		kListedMarketTypeSZE,
	};
	inline bool IsZeroStr(const char* str){
		return str && str[0] == '0' && str[1] == '\0';
	}
	inline bool IsEmpty(const char* str){
		return (str == NULL || *str == '\0');
	}
	kListedMarketType GetListedMarketType(const char* listedMarket)
	{
		if (strcmp(listedMarket, CBondContainer::lmCIB)==0)return kListedMarketTypeCIB;
		else if (strcmp(listedMarket, CBondContainer::lmSSE) == 0)return kListedMarketTypeSSE;
		else if (strcmp(listedMarket, CBondContainer::lmSZE) == 0)return kListedMarketTypeSZE;
		return kListedMarketTypeNone;
	}
	kListedMarketType GetListedMarketTypeByCodeDot(const char* dot)
	{
		if (strcmp(dot, ".IB") == 0)return kListedMarketTypeCIB;
		if (strcmp(dot, ".SH") == 0)return kListedMarketTypeSSE;
		if (strcmp(dot, ".SZ") == 0)return kListedMarketTypeSZE;
		return kListedMarketTypeNone;
	}

	void CheckReportValid(const REPORT& rpt)
	{
		//static const char* cause[2] = { "HASDOT ", "INVALID" };
		//const char* dot = strchr(rpt.m_szCode, '.');
		//int dotpos = dot ? (dot - rpt.m_szCode) : (-1);
		//if (dot)
		//{
		//	int select = 0;
		//	kListedMarketType lm = GetListedMarketType(rpt.m_sListedMarket);
		//	kListedMarketType code = GetListedMarketTypeByCodeDot(dot);
		//	if (lm != code || lm == kListedMarketTypeNone || code == kListedMarketTypeNone)
		//	{
		//		select = 1;
		//	}
		//	int bid = AfxGetBondContainer().GetBondIndex(rpt.m_sBondKey, rpt.m_sListedMarket);
		//	DebugView(0, 0, 0, "\n[REPORT_CHK] <%s>  bid:%d key:%s lm:%s time:%d create:%d pstatus:%d pkid:%d bid[%s-%s] ask[%s-%s]",
		//		cause[select],bid,
		//		rpt.m_sBondKey, rpt.m_sListedMarket,/* rpt.m_szCode, rpt.m_szShortName,*/ rpt.m_time, rpt.m_create_time, rpt.m_price_status, (int)rpt.m_pkg_id,
		//		rpt.m_bidinfo.m_price_status,rpt.m_bidinfo.m_sPrice,
		//		rpt.m_askinfo.m_price_status, rpt.m_askinfo.m_sPrice
		//		);
		//}
	}

}
/*
[内存优化][scofined.qi]
使用vector而不用map,是考虑到以经纪公司id为key,这个个数非常少
用map,每个节点就得多消耗16个字节(三个指针加上2个着色和2个对齐空白字节)
用vector则没有这个开销,另外vector保证数据放在一起,对缓存友好
*/
class ZippedReportMap : public ZippedObjectMap<REPORT, ReportEncoder, kObjectMetaIdReport, EncodeBufInfoVector>{
};

// 如果要使用之前的结构体,需要做几件事情:
// 1.将头文件的成员注释取消
// 2.构造函数中将m_zip分配注释
// 3.将REPORT_MAP_WRAP宏切换成旧版

//#define  REPORT_MAP_WRAP() MapWrapper<ReportMap> map((ReportMap&)m_RepMap.m_map);
#define  REPORT_MAP_WRAP() ZippedReportMap::wrapper map((ZippedReportMap&)*m_zip);

CBondCompanyRep::CBondCompanyRep()
{
	m_zip = new ZippedReportMap();
}

CBondCompanyRep::~CBondCompanyRep()
{
	Clear( );
	if (m_zip)
		delete m_zip;
}

bool CBondCompanyRep::Clear( )
{
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	REPORT_MAP_WRAP();
	map.clear();
	return true;
}

bool CBondCompanyRep::ClearLiquidation(int clrFlag)
{
	if(clrFlag==CBondContainer::LIQ_CLR_BESTPRICE_ALL)
	{
		Clear( );
	}
	else
	{
		qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
		REPORT_MAP_WRAP();
		for (auto pos = map.begin(); pos != map.end();)
		{
			if (clrFlag==CBondContainer::LIQ_CLR_BESTPRICE_BROKE &&  pos->first != "e" && pos->first != "c")
			{//注意：如果只是删除broker的报价，条件需要修改
				pos=map.erase(pos);
			}
			else if(clrFlag==CBondContainer::LIQ_CLR_BESTPRICE_CFETS && pos->first == "c")
			{
				pos = map.erase(pos);
			}
			else if(clrFlag==CBondContainer::LIQ_CLR_BESTPRICE_EXCHANGE && pos->first == "e")
			{
				pos = map.erase(pos);
			}
			else
			{
				++pos;
			}

		}
	}
	return true;
}

bool CBondCompanyRep::InsertCompanyRep(const REPORT& newElement )
{
	qb::base::TempTinyString strCompId(newElement.m_company_id);
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	REPORT_MAP_WRAP();
	map.emplace(strCompId, newElement);
	//m_RepMap.m_map[strCompId] = newElement;
	return true;
}

bool CBondCompanyRep::GetCompanyRepByKey(const std::string& sKey,REPORT &stRep)
{
	qb::base::TempTinyString key(sKey);
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	REPORT_MAP_WRAP();
	if (map.size() == 0)
		return false;
	auto itr = map.find(key);
	if (itr != map.end()){
		stRep = itr->second;
		return true;
	}
	return false;
}

void CBondCompanyRep::ApplyAccess(bool all, const std::string& sKey, ApplyCallback* callback)
{
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	if (all)
	{
		REPORT_MAP_WRAP();
		auto it = map.begin();
		for (; it != map.end(); ++it)
		{
			if (!callback->DoAccess(it->second))
				break;
		}
	}
	else
	{
		REPORT_MAP_WRAP();
		qb::base::TempTinyString key(sKey);
		auto it = map.find(key);
		if (it != map.end())
			callback->DoAccess(it->second);
	}
}
void CBondCompanyRep::ApplyModify(bool all, const std::string& sKey, ApplyCallback* callback)
{
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	REPORT_MAP_WRAP();
	if (all)
	{
		auto it = map.begin();
		for (; it != map.end(); ++it)
		{
			if (!callback->DoModify(it->second))
				break;
		}
	}
	else
	{
		qb::base::TempTinyString key(sKey);
		auto it = map.find(key);
		if (it != map.end())
			callback->DoModify(it->second);
	}
}

int CBondCompanyRep::GetSize()
{ 
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	REPORT_MAP_WRAP();
	return map.size();
}
const char* CBondCompanyRep::GetName()const{
	return "CBondCompanyRep";
}
int	CBondCompanyRep::GetBytes(int mode)
{
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	int bytes = 1;
	REPORT_MAP_WRAP();
	return map.bytes();
}
bool CBondCompanyRep::GetReport( const std::string& sKey,REPORT &stRep, int nFlag) //flag = 0 取最新，1 取最新有效，2 取最新双边
{
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	REPORT_MAP_WRAP();
	if (map.size() == 0) return false;
	std::string tempKey = sKey;
	if (tempKey.empty())
	{
		time_t  tLatestTime = 0;
		for (auto itr = map.begin(); itr != map.end(); ++itr)
		{
			REPORT& report = itr->second;

			if (!IsEmpty(report.m_company_id) &&
				strcmp(report.m_company_id, "e") != 0 && strcmp(report.m_company_id, "c") != 0)
			{
				time_t  tCurTime = max(report.m_time, report.m_create_time);
				bool bBidValid = (report.m_bidinfo.m_price_status[0] != '\0' && strcmp(report.m_bidinfo.m_price_status, "0") != 0);
				bool bOfrValid = (report.m_askinfo.m_price_status[0] != '\0' && strcmp(report.m_askinfo.m_price_status, "0") != 0);
				switch (nFlag)
				{
				case 0:
					if (tCurTime > tLatestTime)
					{//更新
						tempKey = itr->first;
						tLatestTime = tCurTime;
					}
					break;
				case 1:
					if ((bBidValid || bOfrValid) && tCurTime > tLatestTime)
					{//单边有效并更新
						tempKey = itr->first;
						tLatestTime = tCurTime;
					}
					break;
				case 2:
					if (bBidValid && bOfrValid && tCurTime > tLatestTime)
					{//双边有效并更新
						tempKey = itr->first;
						tLatestTime = tCurTime;
					}
					break;
				default:
					//BASSERT(0);
					break;
				}
			}
		}
	}
	if (tempKey.empty())return false;

	qb::base::TempTinyString key(tempKey);
	auto itr = map.find(key);
	if (itr != map.end())
	{
		stRep = itr->second;
		return true;
	}
	return false;
}

bool CBondCompanyRep::GetLatestReport(REPORT &stRep, bool bBuyPool)
{
	bool	bRes = false;
	memset(&stRep, 0, sizeof(REPORT));
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	if (GetSize() <= 0)
		return false;
	time_t  tMaxTime = 0;
	REPORT_MAP_WRAP();
	auto itr = map.begin();
	for (; itr != map.end(); itr++)
	{
		REPORT& rep = itr->second;
		time_t tCurTime = max(rep.m_time, rep.m_create_time);
		if (tMaxTime < tCurTime)
		{
			if (bBuyPool)
			{
				if (IsBidPriceValid(rep) || IsOfrPriceValid(rep))
				{
					tMaxTime = tCurTime;
					stRep = rep;
					bRes = true;
				}
			}
			else
			{
				if (IsBidPriceValid(rep))
				{
					tMaxTime = tCurTime;
					stRep = rep;
					bRes = true;
				}
			}
		}
	}
	return bRes;
}

bool CBondCompanyRep::TestBidPrice(const BLHCONDITION& Cond)
{
	bool all = IsEmpty(Cond.sCompanyid);
	qb::base::TempTinyString key(Cond.sCompanyid);
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	REPORT_MAP_WRAP();
	auto it = all ? map.begin() : map.find(key);
	for (; it != map.end(); ++it)
	{
		const REPORT& rpt = it->second;

		if (IsEmpty(rpt.m_bidinfo.m_yield_price) ||
			strcmp(rpt.m_bidinfo.m_sPrice, "Bid") == 0 ||
			strcmp(rpt.m_bidinfo.m_price_status, "0") == 0/*||strcmp(info.GetEstPrice(),"0")==0*/)
		{//无效价格，或者无效估值
			if (all)
				continue;
		}
		else
		{
			float fBid = atof(rpt.m_bidinfo.m_yield_price);//使用收益率比较，modified by Lance 2020.05.12
			bool bRet = false;
			if (!IsEmpty(Cond.sBPrice) && !IsEmpty(Cond.sBPriceEnd))
			{
				bRet = (fBid > (atof(Cond.sBPrice) - 0.00001));
				if (bRet)
					bRet = (fBid < (atof(Cond.sBPriceEnd) + 0.00001));
			}
			else
			{
				if (!IsEmpty(Cond.sBPrice))
					bRet = (fBid > (atof(Cond.sBPrice) - 0.00001));

				if (!bRet && !IsEmpty(Cond.sBPriceEnd))
					bRet = (fBid < (atof(Cond.sBPriceEnd) + 0.00001));
			}

			if (bRet)
				return bRet;
		}
		if (!all)
			break;
	}
	return false;
}

bool CBondCompanyRep::TestOfrPrice(const BLHCONDITION& Cond)
{
	bool all = IsEmpty(Cond.sCompanyid);
	qb::base::TempTinyString key(Cond.sCompanyid);
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	REPORT_MAP_WRAP();
	auto it = all ? map.begin() : map.find(key);
	for (; it != map.end(); ++it)
	{
		const REPORT& rpt = it->second;

		if (IsEmpty(rpt.m_askinfo.m_yield_price) ||
			strcmp(rpt.m_askinfo.m_sPrice, "Ofr") == 0 ||
			strcmp(rpt.m_askinfo.m_price_status, "0") == 0/*||strcmp(info.GetEstPrice(),"0")==0*/)
		{//无效价格，或者无效估值
			if (all)
				continue;
		}
		else
		{
			float fOfr = atof(rpt.m_askinfo.m_yield_price);//使用收益率比较，modified by Lance 2020.05.12

			bool bRet = false;
			if (!IsEmpty(Cond.sOPrice) && !IsEmpty(Cond.sOPriceEnd))
			{
				bRet = (fOfr > (atof(Cond.sOPrice) - 0.00001));
				if (bRet)
					bRet = (fOfr < (atof(Cond.sOPriceEnd) + 0.00001));
			}
			else
			{
				if (!IsEmpty(Cond.sOPrice))
					bRet = (fOfr > (atof(Cond.sOPrice) - 0.00001));

				if (!bRet && !IsEmpty(Cond.sOPriceEnd))
					bRet = (fOfr < (atof(Cond.sOPriceEnd) + 0.00001));
			}

			if (bRet)
				return bRet;
		}
		if (!all)
			break;
	}

	return false;
}

bool CBondCompanyRep::IsBidPriceValid(const REPORT &stRep)
{
	if (strcmp(stRep.m_bidinfo.m_price_status, "0") == 0)
		return false;

	if (atoi(stRep.m_bidinfo.m_rebate) == 1)
		return true;

	if (!IsEmpty(stRep.m_bidinfo.m_sPrice))
		return true;

	return false;
}

bool CBondCompanyRep::IsOfrPriceValid(const REPORT &stRep)
{
	if (strcmp(stRep.m_askinfo.m_price_status, "0") == 0)
		return false;

	if (atoi(stRep.m_askinfo.m_rebate) == 1)
		return true;

	if (!IsEmpty(stRep.m_askinfo.m_sPrice))
		return true;

	return false;
}
typedef ReportEncoder::FieldArray FieldArray;
time_t CBondCompanyRep::GetModifyTime(const std::string& key)
{
	// 参照CBondUtil::GetModifyTime
	// 4,5对应于REPORT的第4,5个字段,按照顺序
	// 以下静态断言,用于确保report_t结构体中的每个字段不会被意外修改
	// 尤其是顺序,因为GetField严格依赖每个字段对应的顺序
	static_assert(sizeof(price_t) == 478, "sizeof(price_t) must be 478");
	static_assert(sizeof(indx_price_t) ==40, "sizeof(indx_price_t) must be 40");
	static_assert(sizeof(report_t) == 1122, "sizeof(report_t) must be 1122");

	static_assert(FIELD_OFFSET(REPORT, m_dwVersion) == 0, "REPORT.m_dwVersion offset must be 0");
	static_assert(FIELD_OFFSET(REPORT, m_dwMarket) == 4, "REPORT.m_dwMarket offset must be 4");
	static_assert(FIELD_OFFSET(REPORT, m_sBondKey) == 8, "REPORT.m_sBondKey offset must be 8");
	static_assert(FIELD_OFFSET(REPORT, m_sListedMarket) == 41, "REPORT.m_sListedMarket offset must be 41");
	static_assert(FIELD_OFFSET(REPORT, m_time) == 49, "REPORT.m_time offset must be 49");
	static_assert(FIELD_OFFSET(REPORT, m_create_time) == 57, "REPORT.m_create_time offset must be 57");
	static_assert(FIELD_OFFSET(REPORT, m_bp_status) == 65, "REPORT.m_deal_status offset must be 65");
	static_assert(FIELD_OFFSET(REPORT, m_company_id) == 69, "REPORT.m_company_id offset must be 69");
	static_assert(FIELD_OFFSET(REPORT, m_price_status) == 102, "REPORT.m_price_status offset must be 102");
	static_assert(FIELD_OFFSET(REPORT, m_bidinfo) == 105, "REPORT.m_bidinfo offset must be 105");
	static_assert(FIELD_OFFSET(REPORT, m_askinfo) == 583, "REPORT.m_askinfo offset must be 583");
	static_assert(FIELD_OFFSET(REPORT, m_indx_info) == 1061, "REPORT.m_indx_info offset must be 1061");
	static_assert(FIELD_OFFSET(REPORT, m_price_sel) == 1101, "REPORT.m_price_sel offset must be 1101");
	static_assert(FIELD_OFFSET(REPORT, m_tradePrice) == 1102, "REPORT.m_tradePrice offset must be 1102");
	static_assert(FIELD_OFFSET(REPORT, m_pkg_id) == 1114, "REPORT.m_pkg_id offset must be 1114");

	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	if (key.empty())
	{
		time_t tLatestTime = 0;
		EncodeBufInfo infos[16];
		int ncount = m_zip->getall(infos,_countof(infos));
		for (int i = 0; i < ncount; i++)
		{
			FieldArray* fa = (FieldArray*)infos[i].buf;
			if (fa == nullptr)continue;
			const char* companyId = (const char*)fa->GetField((int)kReportField::CompanyId);
			if (!IsEmpty(companyId) &&
				strcmp(companyId, "e") != 0 && strcmp(companyId, "c") != 0)
			{
				time_t t1 = *((const time_t*)fa->GetField((int)kReportField::Time));
				time_t t2 = *((const time_t*)fa->GetField((int)kReportField::CreateTime));
				time_t  tCurTime = max(t1, t2);
				if (tCurTime > tLatestTime)
					tLatestTime = tCurTime;
			}
		}
		return tLatestTime;
	}
	else{
		qb::base::TempTinyString tempKey(key);
		EncodeBufInfo info = m_zip->find(tempKey);
		if (info.buf)
		{
			FieldArray* fa = (FieldArray*)info.buf;
			time_t t1 = *((const time_t*)fa->GetField((int)kReportField::Time));
			time_t t2 = *((const time_t*)fa->GetField((int)kReportField::CreateTime));
			return max(t1, t2);
		}
	}
	return 0;
}
double CBondCompanyRep::GetBuyNetPrice(const std::string& key)
{
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	double idval = MIN_VAL;
	qb::base::TempTinyString tempKey(key);
	EncodeBufInfo info = m_zip->find(tempKey);
	FieldArray* fa = (FieldArray*)info.buf;
	if (fa)
		idval=atof((const char*)fa->GetField((int)kReportField::Bid_clean_price));
	return idval;
}
const void* CBondCompanyRep::GetReportField(const char* key, int index)
{
	qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
	static const char dft_value[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
	qb::base::TempTinyString tempKey(key);
	EncodeBufInfo info = m_zip->find(tempKey);
	if (info.buf && index >= 0 && index<(int)kReportField::Count)
	{
		FieldArray* fa = (FieldArray*)info.buf;
		return fa->GetField(index);
	}
	return dft_value;
}

bool CBondCompanyRep::IsHasPrice(const char* szKey, UINT nBnkFlg)
{
	// UINT	nBnkFlg:BLHCONDITION.nBnkFlg
	EncodeBufInfo infos[16];
	int ncount = 0;
	{
		qb::base::ScopedLock<qb::base::Spinlock> l(m_lock);
		if (szKey == nullptr || IsEmpty(szKey))
			ncount = m_zip->getall(infos, _countof(infos));
		else
		{
			qb::base::TempTinyString tempKey(szKey);
			infos[0] = m_zip->find(tempKey);
			if (infos[0].buf != nullptr)
				ncount = 1;
		}
	}
	if (nBnkFlg == 2){
		for (int i = 0; i < ncount; i++)
		{
			FieldArray* fa = (FieldArray*)infos[i].buf;
			if (!IsEmpty((const char*)fa->GetField((int)kReportField::Idx_price)))
				return true;
		}
	}
	else{
		for (int i = 0; i < ncount; i++)
		{
			FieldArray* fa = (FieldArray*)infos[i].buf;
			const char* bid_price_status = (const char*)fa->GetField((int)kReportField::Bid_price_status);
			const char* ask_price_status = (const char*)fa->GetField((int)kReportField::Ask_price_status);
			if (strcmp(bid_price_status, "0") == 0 && IsZeroStr(ask_price_status))
				continue;
			const char* bid_rebate = (const char*)fa->GetField((int)kReportField::Bid_rebate);
			const char* ask_rebate = (const char*)fa->GetField((int)kReportField::Ask_rebate);
			if ((atoi(bid_rebate) == 1 && !IsZeroStr(bid_price_status)) ||
				(atoi(ask_rebate) == 1 && !IsZeroStr(ask_price_status)))
				return true;

			const char* bid_price = (const char*)fa->GetField((int)kReportField::Bid_sPrice);
			const char* ask_price = (const char*)fa->GetField((int)kReportField::Ask_sPrice);
			if (((!IsEmpty(bid_price) && !IsZeroStr(bid_price_status)) ||
				(!IsEmpty(ask_price) && !IsZeroStr(ask_price_status))))
				return true;
		}
	}
	return false;
}

// 5.6w
/*TODO
static_assert(sizeof(PRICE) == 466, "");
static_assert(sizeof(REPORT) == 1098, "");
static_assert(sizeof(std::string) == 24, "");

static_assert(sizeof(CCompanyRepMap::ReportMap::_Node) == 1100 + 24 + 16, "");
static_assert(sizeof(CCompanyRepMap) ==8, "");
static_assert(sizeof(CBondCompanyRep) == 12, "");
//static_assert(sizeof(ZippedReportMap) == 12, "");
*/



